/**
 * Copyright 2008 Adam Ruggles.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.google.code.sapien.interceptor;

import java.util.List;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.StringUtils;
import org.apache.struts2.StrutsStatics;

import com.google.code.sapien.ActionConstants;
import com.google.code.sapien.model.CategoryAcl;
import com.google.code.sapien.model.User;
import com.google.code.sapien.security.SecurityDetails;
import com.google.code.sapien.security.SapienSecurityDetails;
import com.google.code.sapien.service.CategoryAclService;
import com.google.code.sapien.service.UserService;
import com.google.code.sapien.util.ContextManager;
import com.google.inject.Inject;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.Interceptor;

/**
 * Security Interceptor, initializes the security details and can inject user and security details into the action.
 * @author Adam
 * @version $Id: SecurityInterceptor.java 29 2010-01-04 05:25:44Z a.ruggles $
 * 
 * Created on Nov 22, 2008 at 10:01:14 PM 
 */
public class SecurityInterceptor implements Interceptor, StrutsStatics {
	/**
	 * Serial Version UID.
	 */
	private static final long serialVersionUID = -451550468171754518L;

	/**
	 * The category ACL service.
	 */
	private final CategoryAclService categoryAclService;

	/**
	 * The user service.
	 */
	private final UserService userService;

	/**
	 * Constructs a UserAware interceptor.
	 * @param userService The user service.
	 * @param categoryAclService The category ACL service.
	 */
	@Inject
	public SecurityInterceptor(final UserService userService, final CategoryAclService categoryAclService) {
		this.userService = userService;
		this.categoryAclService = categoryAclService;
	}

	/**
	 * {@inheritDoc}
	 * @see com.opensymphony.xwork2.interceptor.Interceptor#destroy()
	 */
	public void destroy() {
		// Not implemented.
	}

	/**
	 * Returns the cookie value for the persistent login.
	 * @param cookies The cookies from the HTTP request.
	 * @return The value of the cookie or an empty string.
	 */
	private String getCookieValue(final Cookie[] cookies) {
		String cookieMessage = "";
		if (cookies != null) {
			for (Cookie cookie : cookies) {
				if (ActionConstants.USER_PERSISTENT_LOGIN.equals(cookie.getName())) {
					cookieMessage = cookie.getValue();
					break;
				}
			}
		}
		return cookieMessage;
	}
	/**
	 * Returns the online user.
	 * @param context The action context.
	 * @param request The HTTP Servlet request.
	 * @param response The HTTP Servlet response.
	 * @return The online user.
	 */
	private SecurityDetails getOnlineUser(final ActionContext context, final HttpServletRequest request,
			final HttpServletResponse response) {
		// Get the security details.
		SecurityDetails securityDetails = (SecurityDetails) context.getSession().get(ActionConstants.USER_SESSION);
		if (securityDetails == null) {
			// Look for the persistent login cookie.
			final String cookieValue = getCookieValue(request.getCookies());
			// If we found the encrypted cookie message attempt to get the security details for the user.
			if (StringUtils.isNotBlank(cookieValue)) {
				final User user = userService.getPersistentLogin(cookieValue);
				if (user != null) {
					final String persistenceValue = userService.addPersistentLogin(user);
					final Cookie cookie = new Cookie(ActionConstants.USER_PERSISTENT_LOGIN, persistenceValue);
					cookie.setMaxAge(ActionConstants.USER_PERSISTENT_LOGIN_AGE);
					response.addCookie(cookie);
					final List<CategoryAcl> acls = categoryAclService.list(user.getRoles());
					securityDetails = new SapienSecurityDetails(user, acls);
				}
			}
			// If the security details are not found create a anonymous/guest security details.
			if (securityDetails == null) {
				final List<CategoryAcl> acls = categoryAclService.listForGuests();
				securityDetails = new SapienSecurityDetails(acls);
			}
			// Add the security details to the session.
			context.getSession().put(ActionConstants.USER_SESSION, securityDetails);
		}
		return securityDetails;
	}

	/**
	 * {@inheritDoc}
	 * @see com.opensymphony.xwork2.interceptor.Interceptor#init()
	 */
	public void init() {
		// Not implemented.
	}

	/**
	 * Sets the user if one exists in the session.
	 * {@inheritDoc}
	 * @see com.opensymphony.xwork2.interceptor.Interceptor#intercept(com.opensymphony.xwork2.ActionInvocation)
	 */
	public String intercept(final ActionInvocation actionInvocation) throws Exception {
		final Object action = actionInvocation.getAction();
		final ActionContext context = actionInvocation.getInvocationContext();
		final HttpServletRequest request = (HttpServletRequest) context.get(HTTP_REQUEST);
		final HttpServletResponse response = (HttpServletResponse) context.get(HTTP_RESPONSE);
		// Get the online user.
		final SecurityDetails securityDetails = getOnlineUser(context, request, response);
		if (action instanceof UserAware) {
			// Set the IP address from the request.
			securityDetails.setIpAddress(request.getRemoteAddr());
			((UserAware) action).setCurrentUser(securityDetails.getUser());
		}
		try {
			ContextManager.bind(SecurityDetails.class, securityDetails);
			return actionInvocation.invoke();
		} finally {
			ContextManager.unbind(SecurityDetails.class);
		}
	}
}
